package  Hxw3DEngine.core.controller
{
	import flash.geom.Matrix3D;
	import Hxw3DEngine.utils.Matrix3DUtils;
	
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	
	/**
	 * UVN LH 摄像机
	 * */
	public class Camera3D
	{
		private var _posV:Vector3D = null;
		private var _atV:Vector3D = new Vector3D();
		private var _upV:Vector3D = null;
		// UVN 摄像机模型		
		private var _muv:Vector3D = null;
		private var _mvv:Vector3D = null;
		private var _mnv:Vector3D = null;
		// 视图矩阵
		internal var _viweMat:Matrix3D = null;
		// 试图矩阵的逆矩阵
		private var _invertViewMat:Matrix3D = null;
		// 投影矩阵
		internal var _projMat:Matrix3D = null;
		//  视图矩阵的元矩阵
		private var _nMat:Matrix3D = null;
		
		private var _mrdvs:Vector.<Number> = null;
		
		private var _tempV:Vector3D = new Vector3D();
		//是否有变化
		private var _isChange:Boolean = true;
		// 记录坐标系的类型1右手系0左手系
		private var _axisType:int = 1;
		// 用于控制保持原始尺寸的基本高度
		private var _baseViewH:Number = 600;
		// 记录自身的状态码
		private var _statusCode:int = 0;
		public function Camera3D()
		{
			_viweMat = new Matrix3D;
			_projMat = new Matrix3D;
			_nMat = new Matrix3D();
			_mrdvs = new Vector.<Number>( 16, true );
			
			//lookAt( new Vector3D(0, 0, 0), new Vector3D(0, -400, -400), new Vector3D(0, 1, 0) );
			lookAt( new Vector3D(0, 0, 0), new Vector3D(0, -400, -400), new Vector3D(0, 0, -1) );
		}
		/**
		 * 设置基本视口高度,用于缩放视口的时候,场景中显示的物体尺寸保持不变
		 * */
		public function set baseViewHeight(h:Number):void {
			_baseViewH = h;
			_isChange = true;			
		}
		public function set axisType(t:int):void
		{
			_axisType = t;
		}
		public function get axisType():int {
			return _axisType;
		}
		/**
		 * @param			atV		在世界空间中, 视点的有效位置
		 * @param			posV	在世界空间中, 摄像机的位置
		 * @param			upV		用于计算unv三轴的up朝向的矢量
		 * */
		public function lookAt( atV:Vector3D, posV:Vector3D = null, upV:Vector3D = null ):void
		{
			if (posV != null) _posV = posV;
			if (atV != null) _atV.copyFrom(atV);
			if (upV != null) _upV = upV;
			//
			_mnv =  _atV.subtract(_posV);
			_mnv.normalize();
			//
			_muv = _upV.crossProduct( _mnv );
			_muv.normalize();
			//
			_mvv = _mnv.crossProduct( _muv );
			_mvv.normalize();
			//
			_isNegateChange = false;
			_isChange = true;
			update();
		}
		/**
		 * 改变摄像机的视点和摄像机距离视点之间的距离
		 * */
		public function lookAtByDis(atV:Vector3D,dis:Number):void {
			if (atV != null) _atV.copyFrom(atV);
			_posV.copyFrom(_mnv);
			_posV.scaleBy(-dis);
			_posV.incrementBy(_atV);
			//
			_muv = _upV.crossProduct( _mnv );
			_muv.normalize();
			//
			_mvv = _mnv.crossProduct( _muv );
			_mvv.normalize();
			//
			_isNegateChange = false;
			_isChange = true;
		}
		
		/**
		 * 获得拾取射线
		 * @param			px		屏幕坐标的x像素值
		 * @param			py		屏幕坐标的y像素值
		 * @param			pv		世界空间中射线上的点
		 * @param			tv		世界空间中射线的朝向单位矢量
		 * 
		 * */
		public function calcPickingRay(px:Number, py:Number, pv:Vector3D, tv:Vector3D): void
		{
			pv.x = _posV.x;
			pv.y = _posV.y;
			pv.z = _posV.z;
			pv.w = 1;
			//
			px = __nearPlaneW * (px - __screenHalfW) /__screenHalfW;
			py = __nearPlaneH * (__screenHalfH - py) / __screenHalfH;
			// 将摄像机内部坐标转换到摄像机所在的世界坐标
			tv.x = _mnv.x*__near + _muv.x*px + _mvv.x*py;
			tv.y = _mnv.y*__near + _muv.y*px + _mvv.y*py;
			tv.z = _mnv.z*__near + _muv.z*px + _mvv.z*py;
			tv.w = 0;
			tv.normalize();
		}
		/**
		 * 屏幕2d像素坐标转换为世界坐标空间的3d坐标
		 * */
		public function screenToWorldV(px:Number, py:Number):Vector3D {
			
			px = __nearPlaneW * (px - __screenHalfW) /__screenHalfW;
			py = __nearPlaneH * (__screenHalfH - py) / __screenHalfH;
			var pv:Vector3D = new Vector3D(px, py, __near);
			//trace("screenToWorldV, pv: ",pv);
			pv.w = 1;
			//pv = _invertViewMat.transformVector(pv);
			//_invertViewMat.transformVector43(pv, pv);
			Matrix3DUtils.transformVector43(_invertViewMat,pv,pv);
			return pv;
		}
		private var _sz:Number = 1.0;
		/**
		 * 3D世界坐标转换为屏幕的2D像素坐标
		 * */
		public function worldToScreenV(pv:Vector3D):Vector3D {
			//trace("__nearPlaneW,__nearPlaneH: ", __nearPlaneW, __nearPlaneH);
			//pv.w = 1;
			//pv = _viweMat.transformVector(pv);
			//_viweMat.transformVector43(pv, pv);
			Matrix3DUtils.transformVector43(_viweMat,pv,pv);
			_sz = 1 / pv.z;
			pv.x *= _sz;
			pv.y *= _sz;
			pv.x *= _schwsx;// __screenHalfW * _tX;
			pv.y *= _schhsy;// __screenHalfH * _tY;
			pv.x += __screenHalfW;
			pv.y = __screenHalfH - pv.y;
			//
			return pv;
		}
		public function worldToScreenV2(pv:Vector3D):void {
			//trace("__nearPlaneW,__nearPlaneH: ", __nearPlaneW, __nearPlaneH);
			//pv.w = 1;
			//pv = _viweMat.transformVector(pv);
			//_viweMat.transformVector43(pv, pv);
			Matrix3DUtils.transformVector43(_viweMat,pv,pv);
			_sz = 1 / pv.z;
			_sz = 1 / pv.z;
			pv.x *= _sz;
			pv.y *= _sz;
			pv.x *= _schwsx;// __screenHalfW * _tX;
			pv.y *= _schhsy;// __screenHalfH * _tY;
			pv.x += __screenHalfW;
			pv.y = __screenHalfH - pv.y;
		}
		/**
		 * 批量将世界坐标转换为屏幕坐标
		 * */
		public function worldToScreenByVS(invs:Vector.<Number>, outvs:Vector.<Number>, len:int ):void {
			_viweMat.transformVectors(invs, outvs);
			var i:int = 0;
			for (; i < len; ) {
				_tempV.x = outvs[i];
				_tempV.y = outvs[i+1];
				_tempV.z = outvs[i+2];
				
				_tempV.x /= _tempV.z;
				_tempV.y /= _tempV.z;
				_tempV.x *= __screenHalfW * _tX;
				_tempV.y *= __screenHalfH * _tY;
				_tempV.x += __screenHalfW;
				_tempV.y = __screenHalfH - _tempV.y;
				//
				outvs[i] = _tempV.x;
				outvs[i + 1] = _tempV.y;
				//
				i += 3;
			}
		}
		public function getZNear():Number {
			return __near;
		}
		private var __vFOV:Number = 0;
		private var __near:Number = 0;
		public var __far:Number = 0;
		private var __aspect:Number = 1.0;
		// 屏幕的半宽和半高
		private var __screenHalfW:Number = 400.0;
		private var __screenHalfH:Number = 300.0;
		// 近平面
		private var __nearPlaneW:Number = 1.0;
		private var __nearPlaneH:Number = 1.0;
		public function get nearPlaneW():Number {
			return __nearPlaneW;
		}
		public function get nearPlaneH():Number {
			return __nearPlaneH;
		}
		// 记录舞台屏幕的宽和高
		private var _sw:Number = 0;
		private var _sh:Number = 0;
		private var _stageRect:Rectangle = new Rectangle();
		/*
		// 记录摄像机 ccv 的半宽和半高
		private var _ccvHalfW:Number = 0;
		private var _ccvHalfH:Number = 0;
		private var _ccvHalfL:Number = 0;
		private var _ccvMinV:Vector3D = new Vector3D();
		private var _ccvMaxV:Vector3D = new Vector3D();
		*/
		public function perspectiveFieldOfView( fov:Number, sw:int, sh:int, zNear:Number, zFar:Number ):void
		{
			if (sh < 100) {
				sh = 100;
			}
			__screenHalfW = sw * 0.5;
			__screenHalfH = sh * 0.5;
			//
			__near = zNear;
			__far = zFar;
			__vFOV = fov * (Math.PI / 180) * 0.5;
			__aspect = sw / sh;
			_stageRect.width = _sw = sw;
			_stageRect.height = _sh = sh;
			//
			/*
			trace("zNear,zFar,fov: ",zNear,zFar,fov);
			_ccvHalfL = 0.5 * (zFar - zNear);
			_ccvHalfH = 0.5 * (zFar / zNear) * _sh;
			_ccvHalfW = _ccvHalfH * __aspect;
			trace("_ccvHalfW,_ccvHalfH,_ccvHalfL: ", _ccvHalfW, _ccvHalfH, _ccvHalfL);
			_ccvMinV.setTo( -_ccvHalfW, -_ccvHalfH, zNear);
			_ccvMaxV.setTo(_ccvHalfW, _ccvHalfH, zFar);
			*/
			//
			var tY:Number = 1.0 / Math.tan( __vFOV );
			var tX:Number = tY/__aspect;// sh / sw * tY;
			var tZ:Number = zFar / ( zFar - zNear );
			var dZ:Number = -zFar * zNear / ( zFar - zNear );
			//
			__nearPlaneH = __near * Math.tan(__vFOV);
			__nearPlaneW = __nearPlaneH * __aspect;
			//
			var vecData:Vector.<Number> = Vector.<Number>([
				tX,  0,  0, 0,
				0, tY,  0, 0,
				0,  0, tZ, 1,
				0,  0, dZ, 0		
			]);
			//
			_projMat.identity();
			_projMat.copyRawDataFrom( vecData, 0, false );
			//
			_tZ = tZ;
			_tX = tX;
			_tY = tY;
			_dZ = dZ;
			_schwsx = __screenHalfW * _tX;
			_schhsy = __screenHalfH * _tY;
			_isChange = true;
		}
		private var _schwsx:Number = 1.0;
		private var _schhsy:Number = 1.0;
		///*
		private var _tX:Number = 1.0;
		private var _tY:Number = 1.0;
		private var _tZ:Number = 1.0;
		private var _dZ:Number = 1.0;
		//
		public function get perspectiveMatrix():Matrix3D
		{
			return _projMat;
		}
		
		public function get viewMatrix():Matrix3D
		{
			return _viweMat;
		}
		
		private var _worldToClipMatrix:Matrix3D;
		public function get worldToClipMatrix():Matrix3D{
			if(isChange) update();
			if(!_worldToClipMatrix) _worldToClipMatrix = new Matrix3D;
			_worldToClipMatrix.identity();
			_worldToClipMatrix.append(viewMatrix);
			_worldToClipMatrix.append(perspectiveMatrix);
			return _worldToClipMatrix;
		}
		
		/**
		 * 视图矩阵的原单位矩阵(不带平移的矩阵)
		 * */
		public function get nmat():Matrix3D
		{
			return _nMat;
		}
		public function get invertViewMatrix():Matrix3D
		{
			return _invertViewMat;
		}
		private var _viewProjMat:Matrix3D = new Matrix3D();
		private var _isNegateChange:Boolean = false;
		// 视口标准化操作所需的矩阵
		private var _viewNLMat:Matrix3D = new Matrix3D();
		private var _isChanged:Boolean = true;
		private function updateCameraMatrix():void
		{
			
			var vec3Temp:Vector3D;			
			_mnv.normalize();
			// 当右手坐标系的时候起作用
			if (_isNegateChange) {
				_muv.negate();
			}
			vec3Temp = _mnv.crossProduct( _muv );
			_mvv.copyFrom( vec3Temp );
			_mvv.normalize();
			//_mvv.w = 0;
			vec3Temp = _mvv.crossProduct( _mnv );
			_muv.copyFrom( vec3Temp );
			_muv.normalize();
			// 当右手坐标系的时候起作用
			if (_axisType < 0) {
				_isNegateChange = true;
				_muv.negate();
			}
			//
			var tx:Number = -_muv.dotProduct( _posV );
			var ty:Number = -_mvv.dotProduct( _posV );
			var tz:Number = -_mnv.dotProduct( _posV );
			//
			_mrdvs[0] = _muv.x;
			_mrdvs[4] = _muv.y;
			_mrdvs[8] = _muv.z;
			_mrdvs[12] = 0;
			//
			_mrdvs[1] = _mvv.x;
			_mrdvs[5] = _mvv.y;
			_mrdvs[9] = _mvv.z;
			_mrdvs[13] = 0;
			//
			_mrdvs[2] = _mnv.x;
			_mrdvs[6] = _mnv.y;
			_mrdvs[10] = _mnv.z;
			_mrdvs[14] = 0;
			//
			_mrdvs[3] = 0;
			_mrdvs[7] = 0;
			_mrdvs[11] = 0;
			_mrdvs[15] = 1;
			//
			_nMat.copyRawDataFrom(_mrdvs, 0, false );
			// 实际的视图矩阵
			_mrdvs[12] = tx;
			_mrdvs[13] = ty;
			_mrdvs[14] = tz;
			_viweMat.copyRawDataFrom(_mrdvs, 0, false );
			//
			if(_baseViewH > 0){
				_viewNLMat.identity();
				_viewNLMat.appendScale(_baseViewH/_sh,_baseViewH/_sh,1.0);			
				_viweMat.append(_viewNLMat);
				_nMat.append(_viewNLMat);
			}
			_viewProjMat.identity();
			_viewProjMat.append( _viweMat );
			_viewProjMat.append(_projMat);
			//
			_invertViewMat = new Matrix3D();
			_invertViewMat.copyFrom(_viweMat);
			//
			_invertViewMat.invert();
			//
			_isChanged = true;
			//
			_statusCode ++;
		}
		public function get viewProjMat():Matrix3D {
			return _viewProjMat;
		}
		/**
		 * 更新到最新的状态
		 * */
		public function update():void {
			if (_isChange) {
				_isChange = false;				
				updateCameraMatrix();
			}
		}
		private var _tempMat:Matrix3D = new Matrix3D();
		/**
		 * 摄像机本身旋转相应的角度(绕对应的UVN轴)
		 * @param				dAngle		角度变化量
		 * @param				strAxis		"X" 对应U轴,"Y"对应V轴,"Z"对应N轴
		 * */
		public function appendRotByUNV( dAngle:Number, strAxis:String  ):void
		{
			_tempMat.identity();
			switch( strAxis )
			{
				case 'X':
				case 'U':
					_tempMat.appendRotation( dAngle, _muv );
					transformVector( _tempMat, _mvv );
					transformVector( _tempMat, _mnv );
					break;
				case 'Y':
				case 'V':
					_tempMat.appendRotation( dAngle, _mvv );
					transformVector(_tempMat, _muv );
					transformVector(_tempMat, _mnv );
					break;
				case 'Z':
				case 'N':
					_tempMat.appendRotation( dAngle, _mnv );
					transformVector(_tempMat, _muv );
					transformVector(_tempMat, _mvv );
					break;
			}
			_isChange = true;
		}
		/**
		 * 摄像机绕指定点(pivotV可以是视点)及指定轴(axisV)旋转滚动
		 * */
		public function appendRoll( dAngle:Number, axisV:Vector3D, pivotV:Vector3D = null  ):void {
			if (axisV != null) {
				_tempMat.identity();
				//
				_tempMat.appendRotation(dAngle, axisV);
				//
				transformVector(_tempMat, _mnv );
				transformVector(_tempMat, _muv );
				transformVector(_tempMat, _mvv );
				//
				_isChange = true;
			}			
		}
		private var _t0v:Vector3D = new Vector3D();
		private var _t1v:Vector3D = new Vector3D();
		/**
		 * 对着视点绕某轴转动摄像机
		 * */
		public function appendRotPosByAtV(ang:Number, axis:Vector3D, atv:Vector3D = null):void {
			
			_tempMat.identity();
			_tempMat.appendRotation(ang, axis, _atV);
			//
			_t0v = _tempMat.transformVector(_posV);
			_t1v = _tempMat.deltaTransformVector(_upV);
			//
			lookAt(atv,_t0v,_t1v);
		}
		public function appendTranslate( spd:Number, strAxis:String ):void
		{
			switch( strAxis )
			{
				case 'X':
					_posV.x += spd;
					break;
				case 'Y':
					_posV.y += spd;
					break;
				case 'Z':
					_posV.z += spd;
					break;
			}
			_isChange = true;
		}
		/**
		 * 摄像机按照一定的速度大小前进
		 * */
		public function forward( dSpeed:Number ):void
		{
			_posV.x += _mnv.x * dSpeed;
			_posV.y += _mnv.y * dSpeed;
			_posV.z += _mnv.z * dSpeed;
			_isChange = true;
		}
		/**
		 * 摄像机按照一定的速度大小后退
		 * */
		public function back( dSpeed:Number ):void
		{
			_posV.x += -_mnv.x * dSpeed;
			_posV.y += -_mnv.y * dSpeed;
			_posV.z += -_mnv.z * dSpeed;
			_isChange = true;
		}
		
		public function backWard(value:Number):void{
			_posV.z += value;
			_isChange = true;
		}
		
		/**
		 * 按速度移动
		 * */
		public function moveByV(spdv:Vector3D):void {
			_posV.x += spdv.x;
			_posV.y += spdv.y;
			_posV.z += spdv.z;
			_isChange = true;
		}
		public function setPosition(px:Number,py:Number,pz:Number):void {
			_posV.x = px;
			_posV.y = py;
			_posV.z = pz;
			_isChange = true;
		}
		
		//
		public function getPosV():Vector3D {
			return _posV;
		}
		public function getAtV():Vector3D {
			return _atV;
		}
		public function getUpV():Vector3D {
			return _upV;
		}
		
		private function transformVector( mat:Matrix3D, vec3:Vector3D ):void
		{
			var vec3Temp:Vector3D = mat.deltaTransformVector( vec3 );
			
			vec3.copyFrom( vec3Temp );
		}
		public function getUV(uv:Vector3D):void {
			uv.x = _muv.x;
			uv.y = _muv.y;
			uv.z = _muv.z;
		}
		
		
		public function get postion():Vector3D{
			return _posV;
		}
		
		public function getVV(vv:Vector3D):void {
			vv.x = _mvv.x;
			vv.y = _mvv.y;
			vv.z = _mvv.z;
		}
		public function getNV(nv:Vector3D):void {
			nv.x = _mnv.x;
			nv.y = _mnv.y;
			nv.z = _mnv.z;
		}
		public function get isChange():Boolean {
			return _isChanged;
		}
		
		public function set isChange(value:Boolean):void{
			_isChange = value;
		}
		
		// 单帧渲染结束的时候用于设置相关状态
		public function renderEnd():void {
			_isChanged = false;
		}
		//
		private var _tempV3:Vector3D = new Vector3D();
		//private var _tav3:Vector3D = new Vector3D();
		private var _flag:Number = 0;
		private var _rect:Rectangle = new Rectangle();
		// 判定是否和某一几何边界对象碰触了
//		public function hitTestBounds(b:GeomBounds):Boolean {
//			// 先剔除摄像机背面的显示对象
//			_tempV3.copyFrom(b.wcv);
//			_tempV3.decrementBy(_posV);
//			_flag = _tempV3.dotProduct(_mnv);
//			if (_flag > 0) {
//				// 将点 _tempV3, 换到 摄像机空间
//				_tempV3.copyFrom(b.wcv);
//				worldToScreenV2(_tempV3);
//				//b.scrx = _tempV3.x;
//				//b.scry = _tempV3.y;
//				// 记录包围体在屏幕空间上所占据的矩形区域
//				if (b.srcRect) {
//					b.srcRect.width = 2 * b.radius * _sz * _schwsx;
//					b.srcRect.height = 2 * b.radius * _sz * _schhsy;
//					b.srcRect.x = _tempV3.x - 0.5 * b.srcRect.width;
//					b.srcRect.y = _tempV3.y - 0.5 * b.srcRect.height;
//					return _stageRect.intersects(b.srcRect);
//				}else{
//					_rect.width = 2 * b.radius * _sz * _schwsx;
//					_rect.height = 2 * b.radius * _sz * _schhsy;
//					_rect.x = _tempV3.x - 0.5 * _rect.width;
//					_rect.y = _tempV3.y - 0.5 * _rect.height;
//					return _stageRect.intersects(_rect);
//				}
//			}else{
//				// 检测距离
//				if (_tempV3.lengthSquared <= b.radiusSquared) {					
//					return true;
//				}else {
//					// 摄像机背面较远的直接不渲染
//					return false;
//				}
//			}
//			return true;
//		}
		//private function 
		/**
		 * 获得状态码
		 * */
		public function getStatusCode():int {
			return _statusCode;
		}
		
		
		private var _ang:Number = 0;
		public function move(dx:Number,dy:Number):void{
			if (Math.abs(dx) > Math.abs(dy)) {
				_ang = dx * 0.15;
				if (axisType > 0) {
					appendRotPosByAtV(_ang,Vector3D.Y_AXIS);
				}else{
					appendRotPosByAtV(-_ang,Vector3D.Y_AXIS);
				}
			}else {
				_ang = dy * 0.2;
				var tv:Vector3D = new Vector3D();
				getUV(tv);
				appendRotPosByAtV(_ang,tv);
			}
			isChange = true;
			update();
		}
		
		
	}
}